home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 313_01 / search.c < prev    next >
C/C++ Source or Header  |  1990-04-21  |  20KB  |  1,003 lines

  1. /* $Header: /nw2/tony/src/stevie/src/RCS/search.c,v 1.16 89/08/06 09:50:51 tony Exp $
  2.  *
  3.  * This file contains various searching-related routines. These fall into
  4.  * three groups: string searches (for /, ?, n, and N), character searches
  5.  * within a single line (for f, F, t, T, etc), and "other" kinds of searches
  6.  * like the '%' command, and 'word' searches.
  7.  */
  8.  
  9. #include "stevie.h"
  10. #include "regexp.h"    /* Henry Spencer's (modified) reg. exp. routines */
  11.  
  12. /*
  13.  * String searches
  14.  *
  15.  * The actual searches are done using Henry Spencer's regular expression
  16.  * library.
  17.  */
  18.  
  19. #define    BEGWORD    "([^a-zA-Z0-9_]|^)"    /* replaces "\<" in search strings */
  20. #define    ENDWORD    "([^a-zA-Z0-9_]|$)"    /* likewise replaces "\>" */
  21.  
  22. #define    BEGCHAR(c)    (islower(c) || isupper(c) || isdigit(c) || ((c) == '_'))
  23.  
  24. bool_t    begword;    /* does the search include a 'begin word' match */
  25.  
  26. /*
  27.  * mapstring(s) - map special backslash sequences
  28.  */
  29. static char *
  30. mapstring(s)
  31. register char    *s;
  32. {
  33.     static    char    ns[80];
  34.     register char    *p;
  35.  
  36.     begword = FALSE;
  37.  
  38.     for (p = ns; *s ;s++) {
  39.         if (*s != '\\') {    /* not an escape */
  40.             *p++ = *s;
  41.             continue;
  42.         }
  43.         switch (*++s) {
  44.         case '/':
  45.             *p++ = '/';
  46.             break;
  47.  
  48.         case '<':
  49.             strcpy(p, BEGWORD);
  50.             p += strlen(BEGWORD);
  51.             begword = TRUE;
  52.             break;
  53.  
  54.         case '>':
  55.             strcpy(p, ENDWORD);
  56.             p += strlen(ENDWORD);
  57.             break;
  58.  
  59.         default:
  60.             *p++ = '\\';
  61.             *p++ = *s;
  62.             break;
  63.         }
  64.     }
  65.     *p++ = NUL;
  66.  
  67.     return ns;
  68. }
  69.  
  70. static char *laststr = NULL;
  71. static int lastsdir;
  72.  
  73. static LPTR *
  74. ssearch(dir,str)
  75. int    dir;    /* FORWARD or BACKWARD */
  76. char    *str;
  77. {
  78.     LPTR    *bcksearch(), *fwdsearch();
  79.     LPTR    *pos;
  80.     char    *old_ls = laststr;
  81.  
  82.     reg_ic = P(P_IC);    /* tell the regexp routines how to search */
  83.  
  84.     laststr = strsave(str);
  85.     lastsdir = dir;
  86.  
  87.     if (old_ls != NULL)
  88.         free(old_ls);
  89.  
  90.     if (dir == BACKWARD) {
  91.         smsg("?%s", laststr);
  92.         pos = bcksearch(mapstring(laststr));
  93.     } else {
  94.         smsg("/%s", laststr);
  95.         pos = fwdsearch(mapstring(laststr));
  96.     }
  97.  
  98.     /*
  99.      * This is kind of a kludge, but its needed to make
  100.      * 'beginning of word' searches land on the right place.
  101.      */
  102.     if (pos != NULL && begword) {
  103.         if (pos->index != 0 || !BEGCHAR(pos->linep->s[0]))
  104.             pos->index += 1;
  105.     }
  106.     return pos;
  107. }
  108.  
  109. bool_t
  110. dosearch(dir,str)
  111. int    dir;
  112. char    *str;
  113. {
  114.     LPTR    *p;
  115.  
  116.     if (str == NULL)
  117.         str = laststr;
  118.  
  119.     got_int = FALSE;
  120.  
  121.     if ((p = ssearch(dir,str)) == NULL) {
  122.         if (got_int)
  123.             msg("Interrupt");
  124.         else
  125.             msg("Pattern not found");
  126.  
  127.         got_int = FALSE;
  128.         return FALSE;
  129.     } else {
  130.         LPTR savep;
  131.  
  132.         cursupdate();
  133.         /*
  134.          * if we're backing up, we make sure the line we're on
  135.          * is on the screen.
  136.          */
  137.         setpcmark();
  138.         *Curschar = savep = *p;
  139.         set_want_col = TRUE;
  140.         cursupdate();
  141.  
  142.         return TRUE;
  143.     }
  144. }
  145.  
  146. #define    OTHERDIR(x)    (((x) == FORWARD) ? BACKWARD : FORWARD)
  147.  
  148. bool_t
  149. repsearch(flag)
  150. int    flag;
  151. {
  152.     int    dir = lastsdir;
  153.     bool_t    found;
  154.  
  155.     if ( laststr == NULL ) {
  156.         beep();
  157.         return FALSE;
  158.     }
  159.  
  160.     found = dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);
  161.  
  162.     /*
  163.      * We have to save and restore 'lastsdir' because it gets munged
  164.      * by ssearch() and winds up saving the wrong direction from here
  165.      * if 'flag' is true.
  166.      */
  167.     lastsdir = dir;
  168.  
  169.     return found;
  170. }
  171.  
  172. /*
  173.  * regerror - called by regexp routines when errors are detected.
  174.  */
  175. void
  176. regerror(s)
  177. char    *s;
  178. {
  179.     emsg(s);
  180. }
  181.  
  182. static LPTR *
  183. fwdsearch(str)
  184. register char    *str;
  185. {
  186.     static LPTR    infile;
  187.     register LPTR    *p;
  188.     regexp    *prog;
  189.  
  190.     register char    *s;
  191.     register int    i;
  192.  
  193.     if ((prog = regcomp(str)) == NULL) {
  194.         emsg("Invalid search string");
  195.         return NULL;
  196.     }
  197.  
  198.     p = Curschar;
  199.     i = Curschar->index + 1;
  200.     do {
  201.         s = p->linep->s + i;
  202.  
  203.         if (regexec(prog, s, i == 0)) {        /* got a match */
  204.             infile.linep = p->linep;
  205.             infile.index = (int) (prog->startp[0] - p->linep->s);
  206.             free((char *)prog);
  207.             return (&infile);
  208.         }
  209.         i = 0;
  210.  
  211.         if (got_int)
  212.             goto fwdfail;
  213.  
  214.     } while ((p = nextline(p)) != NULL);
  215.  
  216.     /*
  217.      * If wrapscan isn't set, then don't scan from the beginning
  218.      * of the file. Just return failure here.
  219.      */
  220.     if (!P(P_WS))
  221.         goto fwdfail;
  222.  
  223.     /* search from the beginning of the file to Curschar */
  224.     for (p = Filemem; p != NULL ;p = nextline(p)) {
  225.         s = p->linep->s;
  226.  
  227.         if (regexec(prog, s, TRUE)) {        /* got a match */
  228.             infile.linep = p->linep;
  229.             infile.index = (int) (prog->startp[0] - s);
  230.             free((char *)prog);
  231.             return (&infile);
  232.         }
  233.  
  234.         if (p->linep == Curschar->linep)
  235.             break;
  236.  
  237.         if (got_int)
  238.             goto fwdfail;
  239.     }
  240.  
  241. fwdfail:
  242.     free((char *)prog);
  243.     return NULL;
  244. }
  245.  
  246. static LPTR *
  247. bcksearch(str)
  248. char    *str;
  249. {
  250.     static LPTR    infile;
  251.     register LPTR    *p = &infile;
  252.     register char    *s;
  253.     register int    i;
  254.     register char    *match;
  255.     regexp    *prog;
  256.  
  257.     /* make sure str isn't empty */
  258.     if (str == NULL || *str == NUL)
  259.         return NULL;
  260.  
  261.     if ((prog = regcomp(str)) == NULL) {
  262.         emsg("Invalid search string");
  263.         return NULL;
  264.     }
  265.  
  266.     *p = *Curschar;
  267.     if (dec(p) == -1) {    /* already at start of file? */
  268.         *p = *Fileend;
  269.         p->index = strlen(p->linep->s) - 1;
  270.     }
  271.  
  272.     if (begword)        /* so we don't get stuck on one match */
  273.         dec(p);
  274.  
  275.     i = p->index;
  276.  
  277.     do {
  278.         s = p->linep->s;
  279.  
  280.         if (regexec(prog, s, TRUE)) {    /* match somewhere on line */
  281.  
  282.             /*
  283.              * Now, if there are multiple matches on this line,
  284.              * we have to get the last one. Or the last one
  285.              * before the cursor, if we're on that line.
  286.              */
  287.             match = prog->startp[0];
  288.  
  289.             while (regexec(prog, prog->endp[0], FALSE)) {
  290.                 if ((i >= 0) && ((prog->startp[0] - s) > i))
  291.                     break;
  292.                 match = prog->startp[0];
  293.             }
  294.  
  295.             if ((i >= 0) && ((match - s) > i)) {
  296.                 i = -1;
  297.                 continue;
  298.             }
  299.  
  300.             infile.linep = p->linep;
  301.             infile.index = (int) (match - s);
  302.             free((char *)prog);
  303.             return (&infile);
  304.         }
  305.         i = -1;
  306.  
  307.         if (got_int)
  308.             goto bckfail;
  309.  
  310.     } while ((p = prevline(p)) != NULL);
  311.  
  312.     /*
  313.      * If wrapscan isn't set, bag the search now
  314.      */
  315.     if (!P(P_WS))
  316.         goto bckfail;
  317.  
  318.     /* search backward from the end of the file */
  319.     p = prevline(Fileend);
  320.     do {
  321.         s = p->linep->s;
  322.  
  323.         if (regexec(prog, s, TRUE)) {    /* match somewhere on line */
  324.  
  325.             /*
  326.              * Now, if there are multiple matches on this line,
  327.              * we have to get the last one.
  328.              */
  329.             match = prog->startp[0];
  330.  
  331.             while (regexec(prog, prog->endp[0], FALSE))
  332.                 match = prog->startp[0];
  333.  
  334.             infile.linep = p->linep;
  335.             infile.index = (int) (match - s);
  336.             free((char *)prog);
  337.             return (&infile);
  338.         }
  339.  
  340.         if (p->linep == Curschar->linep)
  341.             break;
  342.  
  343.         if (got_int)
  344.             goto bckfail;
  345.  
  346.     } while ((p = prevline(p)) != NULL);
  347.  
  348. bckfail:
  349.     free((char *)prog);
  350.     return NULL;
  351. }
  352.  
  353. /*
  354.  * dosub(lp, up, cmd)
  355.  *
  356.  * Perform a substitution from line 'lp' to line 'up' using the
  357.  * command pointed to by 'cmd' which should be of the form:
  358.  *
  359.  * /pattern/substitution/g
  360.  *
  361.  * The trailing 'g' is optional and, if present, indicates that multiple
  362.  * substitutions should be performed on each line, if applicable.
  363.  * The usual escapes are supported as described in the regexp docs.
  364.  */
  365. void
  366. dosub(lp, up, cmd)
  367. LPTR    *lp, *up;
  368. char    *cmd;
  369. {
  370.     LINE    *cp;
  371.     char    *pat, *sub;
  372.     regexp    *prog;
  373.     int    nsubs;
  374.     bool_t    do_all;        /* do multiple substitutions per line */
  375.  
  376.     /*
  377.      * If no range was given, do the current line. If only one line
  378.      * was given, just do that one.
  379.      */
  380.     if (lp->linep == NULL)
  381.         *up = *lp = *Curschar;
  382.     else {
  383.         if (up->linep == NULL)
  384.             *up = *lp;
  385.     }
  386.  
  387.     pat = ++cmd;        /* skip the initial '/' */
  388.  
  389.     while (*cmd) {
  390.         if (*cmd == '\\')    /* next char is quoted */
  391.             cmd += 2;
  392.         else if (*cmd == '/') {    /* delimiter */
  393.             *cmd++ = NUL;
  394.             break;
  395.         } else
  396.             cmd++;        /* regular character */
  397.     }
  398.  
  399.     if (*pat == NUL) {
  400.         emsg("NULL pattern specified");
  401.         return;
  402.     }
  403.  
  404.     sub = cmd;
  405.  
  406.     do_all = FALSE;
  407.  
  408.     while (*cmd) {
  409.         if (*cmd == '\\')    /* next char is quoted */
  410.             cmd += 2;
  411.         else if (*cmd == '/') {    /* delimiter */
  412.             do_all = (cmd[1] == 'g');
  413.             *cmd++ = NUL;
  414.             break;
  415.         } else
  416.             cmd++;        /* regular character */
  417.     }
  418.  
  419.     reg_ic = P(P_IC);    /* set "ignore case" flag appropriately */
  420.  
  421.     if ((pr